/**
 * 类型推论
 * 1. 声明了一个变量但是没有定义类型
 * 2. TypeScript 会在没有明确的指定类型的时候推测出一个类型，这就是类型推论
 * 3. 如果你声明变量没有定义类型也没有赋值这时候TS会推断成any类型,可以进行任何操作
 */

/**
 * 类型别名
 * type 关键字（可以给一个类型定义一个名字）多用于符合类型
 */

// 1. 定义类型别名
type str = string
let s: str = '我是苏天帝'
console.log(s);

// 2. 定义函数别名
type str1 = () => string
let s1: str1 = () => '我是苏天帝'
console.log(s1());

// 3. 定义值的别名
type value = boolean | 0 | '213'
let s2: value = true
console.log(s2); // true 类型匹配
// let s3: value = '0'
// console.log(s3); // false 类型不匹配

// 4. 类型别名也可以是泛型
type Container<T> = { value: T }

/**
 * 接口 vs. 类型别名
 * 1. 定义方式：
 *      接口: 使用 interface 关键字定义，可以描述对象的结构，也可以用于类（通过 implements 关键字）。
 *      类型别名: 使用 type 关键字定义，可以为基本类型、联合类型、元组、字面量类型等创建别名。
 * 2. 扩展性：
 *      接口: 可以被扩展（使用 extends 关键字），允许你合并多个接口的特性。
 *      类型别名: 不能被扩展，但可以使用交叉类型（使用 & 操作符）来组合多个类型的特性。
 * 3. 实现：
 *      接口: 可以被类实现，这是面向对象编程中的一个特性。
 *      类型别名: 不能被类实现，它们仅仅是类型的另一种表达方式
 * 4. 重新声明：
 *      接口: 可以被重新声明（合并），这意味着你可以多次声明同一个接口名称，并且这些声明会被合并。
 *      类型别名: 不能被重新声明，每个类型别名必须是唯一的。
 * 5. 使用场景：
 *      接口: 更适合用于定义对象的结构，特别是当这个结构需要被多个类实现时。
 *      类型别名: 更适合用于定义更简单的类型，或者当你需要创建一个类型，但不需要使用面向对象的特性时。
 * 6. 接口创建了一个新的名字，可以在其它任何地方使用。
 * 7. 类型别名只是给一个类型定义了一个新的名字，并不会创建新的类型。
 * 8. 类型别名并不创建新名字—比如，错误信息就不会使用别名
 * 9. 类型别名不能被extends和implements（自己也不能extends和implements其它类型）。
 * 10.无法通过接口来描述一个类型并且需要使用联合类型或元组类型，这时通常会使用类型别名。
 */